#include <openssl/evp.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    OpenSSL_add_all_algorithms();

    // 1. 初始化加密上下文
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
        printf("Failed to create EVP_CIPHER_CTX\n");
        return -1;
    }
    EVP_CIPHER_CTX_init(ctx);

    // const EVP_CIPHER *cipher_type = EVP_aes_256_cbc();
    const EVP_CIPHER *cipher_type = EVP_aes_128_cbc();

    // 设置对称加密密钥
    unsigned char key[32] = "your 256-bit key here";
    unsigned char iv[EVP_MAX_IV_LENGTH];
    // 设置明文
    unsigned char *plaintext = (unsigned char *)"This is a test";
    int plaintext_len = strlen((const char *)plaintext);
    unsigned char *ciphertext = malloc(plaintext_len + EVP_MAX_BLOCK_LENGTH);
    int ciphertext_len;

    if (!ciphertext) {
        printf("Failed to allocate memory for ciphertext\n");
        return -1;
    }

    // 设置初始化向量
    if (1 != RAND_bytes(iv, EVP_CIPHER_iv_length(cipher_type))) {
        printf("Failed to generate IV\n");
        return -1;
    }

    // 2. 初始化加密操作
    if (EVP_EncryptInit_ex(ctx, cipher_type, NULL, key, iv) != 1) {
        printf("Failed to initialize encryption\n");
        return -1;
    }

    // 3. 执行加密操作
    int update_len = 0;
    if (EVP_EncryptUpdate(ctx, ciphertext, &update_len, plaintext, plaintext_len) != 1) {
        printf("Failed to update encryption\n");
        return -1;
    }

    // 4. 处理最后一段数据
    int final_len = 0;
    if (EVP_EncryptFinal_ex(ctx, ciphertext + update_len, &final_len) != 1) {
        printf("Failed to finalize encryption\n");
        return -1;
    }

    ciphertext_len = update_len + final_len;

    printf("plaintext_len = %d\n", plaintext_len);
    printf("update_len = %d, final_len = %d\n", update_len, final_len);
    printf("Ciphertext (%d bytes):\n", ciphertext_len);
    for (int i = 0; i < ciphertext_len; ++i) {
        printf("%02x", ciphertext[i]);
    }
    printf("\n");

    EVP_CIPHER_CTX_cleanup(ctx);
    EVP_CIPHER_CTX_free(ctx);
    free(ciphertext);

    return 0;
}